home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 January: Mac OS SDK / Dev.CD Jan 98 SDK1.toast / What's New / Sample Code / Processes / ProcDoggie2.1b2 / UEmergMem.p < prev    next >
Encoding:
Text File  |  1997-11-03  |  16.8 KB  |  454 lines  |  [TEXT/CWIE]

  1. UNIT UEmergMem;
  2.  
  3. {-------------------------------------------------------------------------------
  4.     File:        UEmergMem.p
  5.  
  6.     Contains:    Emergency memory routines.
  7.  
  8.     Written by:    Forrest Tanaka
  9.  
  10.     Copyright:    © 1988-1997 by Apple Computer, Inc., all rights reserved.
  11.  
  12.     Change History (most recent first):
  13.  
  14.     You may incorporate this sample code into your applications without
  15.     restriction, though the sample code has been provided "AS IS" and the
  16.     responsibility for its operation is 100% yours.  However, what you are
  17.     not permitted to do is to redistribute the source as "DSC Sample Code"
  18.     after having made changes. If you're going to re-distribute the source,
  19.     we require that you make it clear in the source that the code was
  20.     descended from Apple Sample Code, but that you've made changes.
  21. --------------------------------------------------------------------------------
  22. #
  23. # EmergMem contains routines to handle emergency memory situations.  This is
  24. # used for Toolbox routines that either don’t check for memory-full errors, or
  25. # that call _SysErr when they can’t allocate the memory that they need.  The
  26. # purpose of the routines in this unit is to make sure that these toolbox
  27. # routines always get the memory they need.
  28. #
  29. -------------------------------------------------------------------------------}
  30. {[j=20/57/1$] Pasmat Options}
  31.  
  32.  
  33. INTERFACE
  34.  
  35.  
  36. (*******************************************************************************
  37. * Used Units
  38. *******************************************************************************)
  39.  
  40.     USES
  41.         Memory
  42.         ;
  43.  
  44. (*******************************************************************************
  45. * Constants
  46. *******************************************************************************)
  47.  
  48.     CONST
  49.         kAllocApp = TRUE; {For NewPtrMargin/NewHandleMargin for app heap alloc}
  50.         kAllocClr = TRUE; {For NewPtrMargin/NewHandleMargin to clear mem block}
  51.  
  52.  
  53. (*******************************************************************************
  54. * ConnectAppGZ - Connect the application grow zone proc
  55. *
  56. * This routine is called whenever this application’s simple grow-zone procedure
  57. * (see below for the source for the grow-zone procedure) is to be
  58. * connected.  From this point on, any requests for memory by this application or
  59. * the system invoke our grow-zone procedure if there isn’t enough memory to
  60. * satisfy the request.
  61. *******************************************************************************)
  62.  
  63.     PROCEDURE ConnectAppGZ;
  64.  
  65.  
  66. (*******************************************************************************
  67. * DisconnectAppGZ - Disconnect the application grow zone proc
  68. *
  69. * This routine is called whenever this application’s simple grow-zone procedure
  70. * (see below for the source for the grow-zone procedure) is to be
  71. * disconnected.  From this point on, any requests for memory by this application
  72. * or the system return memFullErr if there isn’t enough memory to satisfy the
  73. * request.
  74. *******************************************************************************)
  75.  
  76.     PROCEDURE DisconnectAppGZ;
  77.  
  78.  
  79. (*******************************************************************************
  80. * InitEmergMem - Allocate emergency memory
  81. *
  82. * This is called at startup time to allocate the emergency memory block that’s
  83. * deallocated in the grow zone procedure (this application’s grow-zone procedure
  84. * is a privately-declared procedure defined below).  InitEmergMem
  85. * also installs this application’s grow-zone proc.
  86. *
  87. * If there isn’t enough memory to allocate the block of emergency memory, then
  88. * a subsequent call to FailLowMemory(0) returns TRUE.
  89. *******************************************************************************)
  90.  
  91.     PROCEDURE InitEmergMem;
  92.  
  93.  
  94. (*******************************************************************************
  95. * NoEmergMem - Check to see if emergency memory is being used or not
  96. *
  97. * Before my application attempts to use more memory, I call this routine to
  98. * check if I'm already using my emergency memory.  If so, then I’d better
  99. * prepare to die or get my emergency memory back.
  100. *******************************************************************************)
  101.  
  102.     FUNCTION NoEmergMem: Boolean;
  103.  
  104.  
  105. (*******************************************************************************
  106. * RecoverEmergMem - Recover emergency memory
  107. *
  108. * This is called from the event loop if NoEmergMem indicates that the emergency
  109. * memory was deallocated by this application’s grow-zone procedure.  This
  110. * routine will attempt recover the emergency memory.  If this fails, then some
  111. * usually some application options and commands are disabled until there is
  112. * enough free memory to enable them again.
  113. *******************************************************************************)
  114.  
  115.     PROCEDURE RecoverEmergMem;
  116.  
  117.  
  118. (*******************************************************************************
  119. * FailLowMemory - Is there enough free space in heap to allocate memory?
  120. *
  121. * FailLowMemory is called any time a potentially significant amount of non-
  122. * temporary memory is about to be allocated.  It returns TRUE if there’s enough
  123. * free space in the heap to allocate the requested amount of memory and still
  124. * have a significant amount of free space left over, and if the emergency memory
  125. * isn’t being used.  See below for the definition of “significant
  126. * amount.”  "memRequest" specifies the number of bytes that are about to be
  127. * allocated.
  128. *
  129. * This routine is also used even if the amount of memory about to be allocated
  130. * isn’t clear.  In this case, it’s called after the significant amount of memory
  131. * is allocated and 0 is passed in memRequest.  If FailLowMemory returns TRUE,
  132. * then there’s was enough memory for the requested amount and still leave 32K
  133. * free and the emergency memory allocated.  If FailLowMemory returns FALSE, then
  134. * either there isn’t 32K free, or the emergency memory was deallocated by this
  135. * application’s grow-zone procedure, or both.  This is actually the usual way
  136. * that I use this function, because I normally use it for calls to the Toolbox,
  137. * and there’s usually no reliable way to determine how much memory the Toolbox
  138. * is going to allocate.
  139. *******************************************************************************)
  140.  
  141.     FUNCTION FailLowMemory (memRequest: LongInt): Boolean;
  142.  
  143.  
  144.  
  145. (*******************************************************************************
  146. * NewHandleMargin - Create a new handle without using emergency memory
  147. *
  148. * Many toolbox routines simply call SysErr when they run out of memory.  That’s
  149. * not too cool, so I try to make certain that the memory they need is always
  150. * available by making sure that I never request so much memory that the toolbox
  151. * routines are in danger of running out of memory and calling SysErr.  This is
  152. * achieved by calling NewHandleMargin instead of NewHandle any time a
  153. * relocatable memory block is desired.  NewHandle returns memFullErr in MemErr
  154. * if there isn’t enough free contiguous space to satisfy the request and still
  155. * leave a significant amount of free memory.
  156. *
  157. * NewHandleMargin returns NIL if there isn’t enough memory to allocated a block
  158. * of the size specified by "requestedSize".
  159. *
  160. * If "appHeapAlloc" is kAppHeap, then the block of memory is allocated in the
  161. * application’s heap.  If "appHeapAlloc" is NOT kAppHeap, then the block of
  162. * memory is allocated in the system heap.
  163. *
  164. * If "clearMem" is kAllocClr, then all the bytes in the block of memory are
  165. * cleared to zero.  If NOT kAllocClr is passed, then none of the bytes in the
  166. * block of memory are touched after being allocated.
  167. *******************************************************************************)
  168.  
  169.     FUNCTION NewHandleMargin (requestedSize: Size;
  170.                               appHeapAlloc:  Boolean;
  171.                               clearMem:      Boolean): Handle;
  172.  
  173.  
  174. (*******************************************************************************
  175. * NewPtrMargin - Create a new pointer without using emergency memory
  176. *
  177. * Many toolbox routines simply call SysErr when they run out of memory.  That’s
  178. * not too cool, so I try to make certain that the memory they need is always
  179. * available by making sure that I never request so much memory that the toolbox
  180. * routines are in danger of running out of memory and calling SysErr.  This is
  181. * achieved by calling NewPtr instead of NewHandle any time a non-relocatable
  182. * memory block is desired.  NewHandle returns memFullErr in MemErr if there
  183. * isn’t enough free contiguous space to satisfy the request and still leave a
  184. * significant amount of free memory.
  185. *
  186. * NewptrMargin returns NIL if there isn’t enough memory to allocated a block of
  187. * the size specified by "requestedSize".
  188. *
  189. * If "appHeapAlloc" is kAppHeap, then the block of memory is allocated in the
  190. * application’s heap.  If "appHeapAlloc" is NOT kAppHeap, then the block of
  191. * memory is allocated in the system heap.
  192. *
  193. * If "clearMem" is kAllocClr, then all the bytes in the block of memory are
  194. * cleared to zero.  If NOT kAllocClr is passed, then none of the bytes in the
  195. * block of memory are touched after being allocated.
  196. *******************************************************************************)
  197.  
  198.     FUNCTION NewPtrMargin (requestedSize: Size;
  199.                            appHeapAlloc:  Boolean;
  200.                            clearMem:      Boolean): Ptr;
  201.  
  202.  
  203. IMPLEMENTATION
  204.  
  205.     USES
  206.         OSUtils;
  207.  
  208.  
  209. (*******************************************************************************
  210. * Constants
  211. *******************************************************************************)
  212.  
  213.     CONST
  214.         kEmergMemSize = 32768; {Number of bytes of emergency memory to allocate}
  215.         kMemoryMargin = 32768; {Minimum amount of free memory I allow in the heap}
  216.  
  217.  
  218. (*******************************************************************************
  219. * Global Variables
  220. *******************************************************************************)
  221.  
  222.     VAR
  223.         gEmergMem: Handle; {Handle to block of emergency memory}
  224.  
  225.  
  226. {$S Main}
  227. (*******************************************************************************
  228. * Private: AppGrowZoneProc - Custom grow-zone procedure
  229. *
  230. * This is a very basic grow zone procedure.  My application keeps a reserve
  231. * handle of memory in case the Memory Manager gets a request for some memory
  232. * that is not available in my heap.  If memory were to get tight (<32k), the
  233. * Toolbox will crash the system.  This grow-zone proc tries to thwart that
  234. * possibility by releasing the 32K block of emergency memory if it hasn’t been
  235. * released already and if the amount of memory requested is less than 32K.
  236. * Hopefully, that’s enough to satisfy the memory request.
  237. *
  238. * There are three conditions in which the emergency memory isn’t freed.  If the
  239. * emergency memory is already free, obviously there isn’t much that can be done.
  240. * If the emergency memory is equal to GZSaveHnd, then it was the reallocation of
  241. * emergency memory that caused this grow-zone proc to be called.  So it doesn’t
  242. * make much sense to free it in that case.  If the size of the memory request is
  243. * more than the size of emergency memory, then I don’t bother to free emergency
  244. * memory because I assume that the toolbox handles such huge requests for memory
  245. * properly.  Warning: that isn’t always a good assumption, but that’s not my
  246. * fault.
  247. *
  248. *     WARNING: Register A5 might not be valid when grow-zone procedures
  249. *     are called. Read Technical Note #136 and 208.
  250. *
  251. * The "cbNeeded" parameter is the number of bytes that the Memory Manager needs
  252. * to fulfill the memory request it had received.  The number of bytes actually
  253. * freed by AppGrowZoneProc is returned.
  254. *******************************************************************************)
  255.  
  256.  
  257.     FUNCTION AppGrowZoneProc (cbNeeded: Size): LongInt;
  258.  
  259.         VAR
  260.             theA5: LongInt; {Value of A5 when AppGrowZoneProc is called}
  261.  
  262.     BEGIN
  263.         (* Remember the current value of A5 *)
  264.         theA5 := SetCurrentA5;
  265.  
  266.         (* Free emergency memory if possible *)
  267.         IF (gEmergMem^ <> NIL) & (gEmergMem <> GZSaveHnd) & (cbNeeded <=
  268.                 kEmergMemSize) THEN
  269.             BEGIN
  270.                 EmptyHandle (gEmergMem);
  271.                 AppGrowZoneProc := kEmergMemSize
  272.             END
  273.         ELSE
  274.             AppGrowZoneProc := 0;
  275.  
  276.         (* Restore A5 *)
  277.         theA5 := SetA5 (theA5)
  278.     END;
  279.  
  280.  
  281. {$S Main}
  282. (*******************************************************************************
  283. * Public: ConnectAppGZ
  284. *
  285. * It’s pretty self-explanatory.
  286. *******************************************************************************)
  287.  
  288.     VAR
  289.         gAppGrowZoneUPP: GrowZoneUPP;
  290.         
  291.     PROCEDURE ConnectAppGZ;
  292.  
  293.     BEGIN
  294.         SetGrowZone (gAppGrowZoneUPP);
  295.     END;
  296.  
  297.  
  298. {$S Main}
  299. (*******************************************************************************
  300. * Public: DisconnectAppGZ
  301. *
  302. * It’s pretty self-explanatory.
  303. *******************************************************************************)
  304.  
  305.     PROCEDURE DisconnectAppGZ;
  306.  
  307.     BEGIN
  308.         SetGrowZone (NIL)
  309.     END;
  310.  
  311.  
  312. {$S Startup}
  313. (*******************************************************************************
  314. * Public: InitEmergMem
  315. *
  316. * It’s pretty self-explanatory.
  317. *******************************************************************************)
  318.  
  319.     PROCEDURE InitEmergMem;
  320.  
  321.     BEGIN
  322.         gAppGrowZoneUPP := NewGrowZoneProc(@AppGrowZoneProc);
  323.         gEmergMem := NewHandle (kEmergMemSize);
  324.         ConnectAppGZ
  325.     END;
  326.  
  327.  
  328. {$S Main}
  329. (*******************************************************************************
  330. * Public: NoEmergMem
  331. *
  332. * We check on the handle and the master pointer of gEmergMem to see if the
  333. * emergency memory block has been emptied by AppGrowZoneProc, or was never allocated
  334. * in the first place.
  335. *******************************************************************************)
  336.  
  337.     FUNCTION NoEmergMem: Boolean;
  338.  
  339.     BEGIN
  340.         (* Empty handle means no emergency memory *)
  341.         NoEmergMem := (gEmergMem = NIL) | (gEmergMem^ = NIL)
  342.     END;
  343.  
  344.  
  345. {$S Main}
  346. (*******************************************************************************
  347. * Public: RecoverEmergMem
  348. *
  349. * Not much to describe.
  350. *******************************************************************************)
  351.  
  352.     PROCEDURE RecoverEmergMem;
  353.  
  354.     BEGIN
  355.         ReallocateHandle (gEmergMem, kEmergMemSize);
  356.     END;
  357.  
  358.  
  359. {$S Main}
  360. (*******************************************************************************
  361. * Public: FailLowMemory
  362. *
  363. * PurgeSpace is used to determine how much free memory there’d be in the heap if
  364. * all purgeable blocks were purged.  If this amount is less than the amount
  365. * needed, or if there isn’t any emergency memory, TRUE is returned.
  366. *******************************************************************************)
  367.  
  368.     FUNCTION FailLowMemory (memRequest: LongInt): Boolean;
  369.  
  370.     VAR
  371.         total:  LongInt; {Total amount of free memory if heap was purged}
  372.         contig: LongInt; {Max amount of free contiguous memory if heap was purged}
  373.  
  374.     BEGIN
  375.         PurgeSpace ((*<*)total, (*<*)contig);
  376.         FailLowMemory := (total < (memRequest + kMemoryMargin)) | NoEmergMem
  377.     END;
  378.  
  379.  
  380. {$S Main}
  381. (*******************************************************************************
  382. * Public: NewHandleMargin
  383. *
  384. * I don’t call SysError with an ID 25 if there isn’t enough memory to satisfy
  385. * the request, so there isn’t much reason to use the grow-zone proc.  So, I
  386. * disconnect the grow-zone proc temporarily just before I allocate the memory.
  387. *******************************************************************************)
  388.  
  389.     FUNCTION NewHandleMargin (requestedSize: Size;
  390.                               appHeapAlloc:  Boolean;
  391.                               clearMem:      Boolean): Handle;
  392.  
  393.     BEGIN
  394.         IF FailLowMemory (requestedSize) THEN
  395.             NewHandleMargin := NIL
  396.         ELSE
  397.             BEGIN
  398.                 (* We handle memFullErr properly, so don’t need grow-zone proc *)
  399.                 DisconnectAppGZ;
  400.  
  401.                 (* Allocate the memory with the requested options *)
  402.                 IF (NOT appHeapAlloc) AND clearMem THEN
  403.                     NewHandleMargin := NewHandleSysClear (requestedSize)
  404.                 ELSE IF (NOT appHeapAlloc) THEN
  405.                     NewHandleMargin := NewHandleSys (requestedSize)
  406.                 ELSE IF clearMem THEN
  407.                     NewHandleMargin := NewHandleClear (requestedSize)
  408.                 ELSE
  409.                     NewHandleMargin := NewHandle (requestedSize);
  410.  
  411.                 (* Connect up the grow-zone proc again *)
  412.                 ConnectAppGZ
  413.             END
  414.     END;
  415.  
  416.  
  417. {$S Main}
  418. (*******************************************************************************
  419. * Public: NewPtrMargin
  420. *
  421. * I don’t call SysError with an ID 25 if there isn’t enough memory to satisfy
  422. * the request, so there isn’t much reason to use the grow-zone proc.  So, I
  423. * disconnect the grow-zone proc temporarily just before I allocate the memory.
  424. *******************************************************************************)
  425.  
  426.     FUNCTION NewPtrMargin (requestedSize: Size;
  427.                            appHeapAlloc:  Boolean;
  428.                            clearMem:      Boolean): Ptr;
  429.  
  430.     BEGIN
  431.         IF FailLowMemory (requestedSize) THEN
  432.             NewPtrMargin := NIL
  433.         ELSE
  434.             BEGIN
  435.                 (* We handle memFullErr properly, so don’t need grow-zone proc *)
  436.                 DisconnectAppGZ;
  437.  
  438.                 (* Allocate the memory with the requested options *)
  439.                 IF (NOT appHeapAlloc) AND clearMem THEN
  440.                     NewPtrMargin := NewPtrSysClear (requestedSize)
  441.                 ELSE IF NOT appHeapAlloc THEN
  442.                     NewPtrMargin := NewPtrSys (requestedSize)
  443.                 ELSE IF clearMem THEN
  444.                     NewPtrMargin := NewPtrClear (requestedSize)
  445.                 ELSE
  446.                     NewPtrMargin := NewPtr (requestedSize);
  447.  
  448.                 (* Connect up the grow-zone proc again *)
  449.                 ConnectAppGZ
  450.             END
  451.     END;
  452.  
  453. END.
  454.